package packet;
import java.io.*;
import java.net.*;
import java.util.List;


/** Hlavní třída zajišťující provoz chatovacího serveru. */
public class ChatServer {

    /** Port, na kterém server poběží. */
    public static final int PORT = 10997;

    /** Soket chatovacího serveru. */
    private ServerSocket server;
    /** Seznam připojených klientů. */
    private List<ClientThread> clients;

    /** Spustí server. */
    public void run() {
        clients = new java.util.ArrayList<ClientThread>();
        try {
            server = new ServerSocket(PORT); //vytvořit serverový soket naslouchající na 0.0.0.0

            while(true) {
                Socket s = server.accept(); //přijmout klienta
                ClientThread newclient = new ClientThread(s); //vytvořit vlákno
                clients.add(newclient); //přidat klienta do seznamu
                newclient.start(); //spustit vlákno
                broadcast("Připojen.", newclient); //odeslat ostatním zprávu
            }
        }
        catch(IOException e) {
            e.printStackTrace(System.err);
        }
        finally {
            if(server != null) {
                //odpojit všechny klienty
                for(ClientThread clt: getClients()) clt.close();
                clients.clear();

                try { server.close(); }
                catch(IOException e) {}
            }
        }
    }

    /** Pošle všem kromě odesílatele zprávu. */
    public synchronized void broadcast(String message, ClientThread from) {
        String ip = from.socket.getInetAddress().getHostAddress();
        message = ip + ": " + message;
        for(ClientThread clt: getClients()) {
           if(clt != from)
            clt.out.println(message);
            clt.out.flush();
        }
        System.out.println(message);
    }

    /** Vrací seznam klientů.
      * Tuto metodu je nutné používat místo přímého přístupu k proměnné clients -
      * - ten není synchronizovaný. */
    public synchronized List<ClientThread> getClients() {
        return clients;
    }


    /** Vlákno zajišťující čtení zpráv od klienta. */
    private class ClientThread extends Thread {

        /** Klientův soket. */
        Socket socket;
        /** Výstupní proud. */
        PrintStream out;
        /** Vstupní proud. */
        BufferedReader in;

        /** Vytvoří nové klientské vlákno. */
        public ClientThread(Socket socket) {
            this.socket = socket;
            try {
                out = new PrintStream(socket.getOutputStream()); //vytvořit PrintStream
                in = new BufferedReader(new InputStreamReader(socket.getInputStream())); //vytvořit BufferedReader
            }
            catch(IOException e) {
                e.printStackTrace(System.err);
                close();
            }
        }

        public void run() {
            try {
                while(true) {
                    String message = in.readLine();
                    if(message == null) break;
                    if(message.startsWith("/quit")) break;
                    else broadcast(message, this);
                }
            }
            catch(IOException e) {
                System.err.println("Kvuli chybe odpojen klient.");
                e.printStackTrace(System.err);
            }
            finally {
                close(); //odpojit
            }
        }

        /** Odpojí klienta. */
        public void close() {
            broadcast("Odpojen.", this); //odeslat zprávu o odpojení
            getClients().remove(this); //vymazat ze seznamu
            try {
                out.close(); //zavřít výstupní proud
                in.close(); //zavřít vstupní proud
                socket.close(); //zavřít soket
            } catch(IOException e) {}
        }
    }

    public static void main(String[] args) {
        new ChatServer().run();
    }
} 